#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


void imprimir_ayuda ( );

extern int comprimir_archivo (
		const char *nomb_arch_entrada,
		const char *nomb_arch_salida );

extern int descomprimir_archivo (
		const char *nomb_arch_entrada,
		const char *nomb_arch_salida );

void test_0 ( void );
void test_1 ( void );

typedef void (*test_fn) ( void );

// Tests
test_fn tests[] = {
	test_0,
	test_1,
	NULL };

const char* nombre_programa;

int main ( int argc, char **argv ) {
	int siguiente_opcion;

	// Opciones
	const char* const op_cortas = "cdhvt:T";

	const struct option op_largas[] = {
		{ "comprimir", 		0, NULL, 'h' },
		{ "descomprimir", 	0, NULL, 'h' },
		{ "help", 			0, NULL, 'h' },
		{ "verbose", 		0, NULL, 'v' },
		{ "test", 			1, NULL, 't' },
		{ "test-todos", 	0, NULL, 'T' },
		{ NULL, 0, NULL, 0 }
	};

	// Parametros
	int numero_test;

	// Flags de opciones
	int comprimir = 0;
	int descomprimir = 0;
	int verbose = 0;
	int test = 0;
	int test_todos = 0;

	// Guardar nombre del programa para usarlo en la ayuda
	nombre_programa = argv[0];

	// Si se ejecuta sin parametros ni opciones
	if ( argc == 1 ) {
		imprimir_ayuda ( );

		exit ( EXIT_SUCCESS );
	}

	while ( 1 ) {
		siguiente_opcion = getopt_long ( argc, argv, op_cortas, op_largas, NULL);

		// No hay mas opciones
		if ( siguiente_opcion == -1 )
			break;

		// Procesar opcion
		switch ( siguiente_opcion ) {
			case 'c' : /* -c o --comprimir */
				comprimir = 1;
				break;
			case 'd' : /* -d o --descomprimir */
				descomprimir = 1;
				break;
			case 'h' : /* -h o --help */
				imprimir_ayuda ( );
				exit ( EXIT_SUCCESS );
				break;
			case 'v' : /* -v o --verbose */
				verbose = 1;
				break;
			case 't' : /* -t ó --test */
				test = 1;
				numero_test = atoi ( optarg );
				break;
			case 'T' : /* -T ó --test-todos */
				test_todos = 1;
				break;
			case '?' : /* opcion no valida */
				imprimir_ayuda ( );
				exit ( EXIT_SUCCESS );
			default : /* opcion no valida */
				abort ( );
		}
	}

	if ( comprimir && descomprimir ) {
		imprimir_ayuda ( );
		exit ( EXIT_SUCCESS );
	}

	if ( comprimir ) {
		char *nomb_arch_entrada;
		char nomb_arch_salida[256];

		if ( optind == argc ) {
			imprimir_ayuda ( );
			exit ( EXIT_SUCCESS );
		}

		while ( optind < argc ) {
			nomb_arch_entrada = argv[optind++];

			strcpy ( nomb_arch_salida, nomb_arch_entrada );
			strcat ( nomb_arch_salida, ".oc2" );

			if ( verbose )
				printf ( "Comprimiendo archivo : %s\n", nomb_arch_entrada );

			if ( !comprimir_archivo ( nomb_arch_entrada, nomb_arch_salida ) )
				fprintf ( stderr, "Error al comprimir el archivo: %s\n", nomb_arch_entrada );
		}
	}

	if ( descomprimir ) {
		char *nomb_arch_entrada;
		char nomb_arch_salida[256];

		if ( optind == argc ) {
			imprimir_ayuda ( );
			exit ( EXIT_SUCCESS );
		}

		while ( optind < argc ) {
			nomb_arch_entrada = argv[optind++];

			// Se asume que los archivos de entrada terminan en ".oc2"
			strncpy ( nomb_arch_salida, nomb_arch_entrada, strlen ( nomb_arch_entrada ) - 4 );
			nomb_arch_salida[strlen ( nomb_arch_entrada ) - 4] = 0;

			if ( verbose )
				printf ( "Descomprimiendo archivo : %s\n", nomb_arch_entrada );

			if ( !descomprimir_archivo ( nomb_arch_entrada, nomb_arch_salida ) )
				fprintf ( stderr, "Error al descomprimir el archivo : %s\n", argv[optind] );
		}
	}

	if ( test ) {
		printf ( "Ejecutando test %d...\n", numero_test );

		tests[numero_test] ( );
	}

	if ( test_todos ) {
		printf ( "Ejecutando todos los tests ...\n" );

		unsigned int i;

		for ( i = 0; tests[i]; i++ ) {
			printf ( "Test %d:\n", i );

			tests[i] ( );
		}
	}

	return 0;
}

void imprimir_ayuda ( ) {
	printf ( "Uso: %s opciones [ nombre_archivo ]\n", nombre_programa );
	printf ( "    -c  --comprimir             Comprime archivo de entrada\n" );
	printf ( "    -d  --descomprimir          Descomprime archivo de entrada\n" );
	printf ( "    -h  --help                  Imprime esta ayuda\n" );
	printf ( "    -v  --verbose               Imprime información de proceso\n" );
	printf ( "    -t  --test                  Ejecuta test\n" );
	printf ( "    -T  --test-todos            Ejecuta todos los tests\n" );
}

void test_0 ( void ) {
	printf ( "Test: 0\n" );
}

void test_1 ( void ) {
	printf ( "Test: 1\n" );
}
